#include<stdio.h>
#include<stdlib.h>
#include"typedef.h"
#include"getnum.h"



void pideParam(tipoParametros *parametros);
int pideMem(tipoTablero **tablero, tipoParametros parametros);
int llenaTablero(tipoJuego *juego);
void inicialTablero(tipoTablero *tablero, tipoParametros param);
int creaLibres(tipoTablero *tablero, tipoPunto **puntos);
void imprTablero(tipoTablero *tablero);
void imprimeTablero(tipoJuego *juego, int i);
void bolitasAleat(tipoTablero *tablero, tipoParametros param);
void agrega_bolita(tipoTablero *tablero, int r, int  i, int j);
void copiaEstruct(tipoPunto *a, tipoPunto * b);
int posibleMov(tipoTablero * tablero, tipoPunto origen, tipoPunto destino, int *existe);
int posCamino(tipoTablero * tablero, tipoPunto origen, tipoPunto destino, int *existe);
int pertenMat(tipoTablero * tablero, tipoPunto punto);
int swap(tipoTablero * tablero, tipoPunto origen, tipoPunto destino);
void tomaMov(tipoPunto *origen, tipoPunto *destino);
void actualizaLibresM(tipoTablero * tablero, tipoPunto coordIni, tipoPunto coordFin);
int ceros(tipoTablero * tablero);
int cuentaDiag(tipoTablero * tablero, int color, int i, int j, int orient);
int cuentaVer(tipoTablero * tablero, int color, int i, int j);
int cuentaHor(tipoTablero * tablero, int color, int i, int j);
int elimDiag(tipoTablero * tablero, int i, int j, int * elim, int linea, int color, int orient);
int elimVer(tipoTablero * tablero, int i, int j, int * elim, int linea, int color);
int elimHor(tipoTablero * tablero, int i, int *j, int * elim, int linea, int color);
int posibleElim(tipoTablero * tablero, int *elim, int linea, int cantColores);
int pideIn(tipoJuego *juego, int *movida, int *elim, int jugador);
int validaIn(tipoJuego *juego, tipoTablero * tablero, char *s, int *movida, int *elim, int opcion);
int validMov(char *s, int vec[]);

int
main(void)
{
	tipoPunto origen;
	tipoPunto destino;
	int existe, elim, movida=0;

	int i, j;
	
	tipoJuego juego;
	juego.parametros.opcion=1;
	pideParam(&(juego.parametros));
	llenaTablero(&juego);
	creaLibres(juego.tablero1, &((juego.tablero1)->puntos));
	imprimeTablero(&juego, JUGADOR_1);
	while(juego.tablero1->dim_libres!=0)	
	{	existe=elim=0;
		movida=0;
		//tomaMov(&origen, &destino);
		//printf("linea :%d\n", juego.parametros.linea);
		//posibleMov(juego.tablero1, origen, destino, &existe);
		pideIn(&juego, &movida, &elim, JUGADOR_1);
		posibleElim(juego.tablero1, &elim, juego.parametros.linea, juego.parametros.colores);

		if(elim == 0)
			bolitasAleat(juego.tablero1, juego.parametros);
		imprimeTablero(&juego, JUGADOR_1);
	}
}




int
pideIn(tipoJuego *juego, int *movida, int *elim, int jugador)
{
        int i;
        int letra;
	int ret;
	char *entrada=NULL;
	int opcion;
	opcion=juego->parametros.opcion;

	printf("Jugador %d ingrese la accion a realizar\n", jugador);

	for(i=0; (letra=getchar())!='\n' && letra!=EOF; i++)
	{
		if(i%10==0)
			entrada=realloc(entrada, (i+10)*sizeof(char));
		entrada[i]=letra;
	}
	entrada=realloc(entrada, (i+1)*sizeof(char));
	entrada[i]=0;

	if(jugador== 1)
		ret=validaIn(juego, juego->tablero1, entrada, movida, elim, opcion);
	else if(jugador ==2)
		 ret=validaIn(juego, juego->tablero2, entrada, movida, elim, opcion);
	//imprMsg(ret);
	printf("Imprime mensaje\n");

	return ret;
}
int
validaIn(tipoJuego *juego, tipoTablero * tablero, char *s, int *movida, int *elim, int opcion)
{
	int ret;
	int coord[4];
	tipoPunto punto1, punto2;
	int existe=1;

	if(strcmp(s, "quit")==0)
	{	//ret=quit(juego);
		printf("quit\n");
	}
	else if(strncmp(s, "save ", 5)==0)
	{
		//ret=save(juego, s+5);
		printf("save\n");
	}
	else if(strcmp(s, "undo")==0)
	{	if(opcion != 3)
		{	//ret=undoActivo(juego);
			printf("undo activo\n");
			*movida=0;
		}
		else ret=SIN_UNDO;
	}
	else if(s[0]=='[' && validMov(s, coord))
	{
		printf("movida\n");
		if(*movida==0)
		{	printf("num1 %d, num2 %d, num3 %d, num4 %d\n", coord[0], coord[1], coord[2], coord[3]);
			punto1.x=coord[0];
			punto1.y=coord[1];
			punto2.x=coord[2];
			punto2.y=coord[3];
			
			if(CoordValidas(tablero, punto1, punto2))
			{	
				printf("Pertenece al tablero\n");
				ret=posibleMov(tablero, punto1, punto2, movida);
				if(ret==NO_ERROR_C)
					*movida=1;
				else if(ret == SIN_CAMINO)
					  ret=SIN_CAMINO;
			}
			else
				ret=INVALID_COOR;
			
		}
		else
			ret=MOV_DUPL;
	}
	else
		ret=MAL_INGRESO;

	free(s);

	return ret;
}

int
validMov(char *s, int vec[])
{
	int num1, num2, num3, num4;
	
	sscanf(s, "[%d,%d]->[%d,%d]\n", &num1, &num2, &num3, &num4);

	vec[0]=num1;
	vec[1]=num2;
	vec[2]=num3;
	vec[3]=num4;	

	return 1;
}


int
posibleElim(tipoTablero * tablero, int *elim, int linea, int cantColores)
{
	int i, j, c;
	int limitei, limitej;
	limitei= tablero->fils-linea+1;
	limitej= tablero->cols-linea+1;


	for(c=1; c <= cantColores; c++)
	{	
	  	for(i=0; i < (tablero->fils); i++)
			for(j=0; j< limitej; j++)
				if((tablero->matriz[i][j] == c) || (tablero->matriz[i][j] == -1))
					elimHor(tablero, i, &j, elim, linea, c);
					
		for(i=0; i <= limitei; i++)
			for(j=0; j< (tablero->cols); j++)
				if((tablero->matriz[i][j] == c) || (tablero->matriz[i][j] == -1))
					elimVer(tablero, i, j, elim, linea, c);			

		for(i=0; i <= limitei; i++)
			for(j=0; j<= limitej; j++)

				if((tablero->matriz[i][j] == c) || (tablero->matriz[i][j] == -1))
					elimDiag(tablero, i, j, elim, linea, c, IZQ);
					elimDiag(tablero, i, j, elim, linea, c, DER);
		
					
	ceros(tablero);
	
	}
	
	
	return 0;
}
int
elimHor(tipoTablero * tablero, int i, int *j, int * elim, int linea, int color)
{	int cantH;
	int auxj=*j;

	cantH=cuentaHor(tablero, color,  i, auxj);

	if(cantH >= linea)
	{	while(cantH--)
		{	if(tablero->matriz[i][auxj]==color)
			{	(*elim)++;
				tablero->matriz[i][auxj]=-1;
			}
			auxj++;
		}
		*j=auxj;
	}
	return 0;
}
int
elimVer(tipoTablero * tablero, int i, int j, int * elim, int linea, int color)
{	int cantV;
	int auxi=i;
	
	
	cantV=cuentaVer(tablero, color,  auxi, j);

	if(cantV >= linea)
	{	while(cantV--)
		{	if(tablero->matriz[auxi][j]== color)
			{	(*elim)++;
				tablero->matriz[auxi][j]=-1;
			}
			auxi++;
		}
		i=auxi;
	}
	return 0;
}
int
elimDiag(tipoTablero * tablero, int i, int j, int * elim, int linea, int color, int orient)
{	int cantD;
	int auxi=i;
	int auxj=j;

	cantD=cuentaDiag(tablero, color,  auxi, auxj, orient);

	if(cantD >= linea)
	{	while(cantD--)
		{	if(tablero->matriz[auxi][auxj]== color)	
			{	(*elim)++;
				tablero->matriz[auxi][auxj]=-1;
			}
			auxi++;
			auxj= ((orient==IZQ)? auxj-1 : auxj+1);
		}
		j=auxj;
		i=auxi;
	}
	return 0;
}
int
cuentaHor(tipoTablero * tablero, int color, int i, int j)
{	 
	if( (tablero->matriz[i][j]==color) || (tablero->matriz[i][j]== -1))
		if(j+1 < tablero->cols)
			return 1+ cuentaHor(tablero, color, i, j+1);
		else
			return 1;
	return 0;
}
int
cuentaVer(tipoTablero * tablero, int color, int i, int j)
{
	if( (tablero->matriz[i][j]==color) || (tablero->matriz[i][j]== -1) ) 
		if(i+1 < tablero->fils)
			return 1+ cuentaVer(tablero, color, i+1, j);
		else
			return 1;
	return 0;
}	
int
cuentaDiag(tipoTablero * tablero, int color, int i, int j, int orient)
{
	if( (tablero->matriz[i][j]==color) || (tablero->matriz[i][j]== -1)) 
		j= ((orient==IZQ)? j-1 : j+1);
		if((i+1 < tablero->fils) && (j < tablero->cols) && (j > 0))
			return 1+ cuentaDiag(tablero, color, i+1, j, orient);
		else
			return 1;
	return 0;
}
int
ceros(tipoTablero * tablero)
{	int i, j;
  
	  for(i=0; i < tablero->fils; i++)
		for(j=0; j < tablero->cols; j++)
			if(tablero->matriz[i][j]==-1)
			
			  tablero->matriz[i][j]=0;
	  return 0;
}


void
tomaMov(tipoPunto *origen, tipoPunto *destino)
{
	origen->x=getint("Ingrese la coord x de origen:\n");
	origen->y=getint("Ingrese la coord y de origen:\n");
	destino->x=getint("Ingrese la coord x de destino:\n");
	destino->y=getint("Ingrese la coord y de destino:\n");
}

int
posibleMov(tipoTablero * tablero, tipoPunto origen, tipoPunto destino, int *existe)
{	int colorAux;
	colorAux=tablero->matriz[origen.x][origen.y];
	tablero->matriz[origen.x][origen.y]=0;
	
	posCamino(tablero, origen, destino, existe);
	
	tablero->matriz[origen.x][origen.y]=colorAux;
	
	if(*existe == 1)
	{	swap(tablero, origen, destino);
		ceros(tablero);
		return NO_ERROR_C;
		
	}
	ceros(tablero);
	return NO_CAMINO;
}
	

int 
posCamino(tipoTablero * tablero, tipoPunto origen, tipoPunto destino, int *existe)
{	
	tipoPunto punto;


	if( (origen.x == destino.x) && (origen.y == destino.y) )
	{	*existe=1;

		return 0;
	}

	if((tablero->matriz[origen.x][origen.y] == 0) && (*existe == 0))
	{	tablero->matriz[origen.x][origen.y] = -1;
	  
		punto.x=(origen.x)+1;
		punto.y=(origen.y);
		if(pertenMat(tablero, punto))
			posCamino(tablero, punto, destino, existe);
		
		punto.x=(origen.x);
		punto.y=(origen.y)+1;
		if(pertenMat(tablero, punto))
			posCamino(tablero, punto, destino, existe);
		
		punto.x=(origen.x)-1;
		punto.y=(origen.y);
		if(pertenMat(tablero, punto))
			posCamino(tablero, punto, destino, existe);
	
		punto.x=(origen.x);
		punto.y=(origen.y)-1;
		if(pertenMat(tablero, punto))
			posCamino(tablero, punto, destino, existe);
	
	}	
	return 0;

}
		
int 
CoordValidas(tipoTablero *tablero, tipoPunto origen, tipoPunto destino)
{
	int dentro, origValido, destValido;
	dentro=(pertenMat(tablero, origen) && pertenMat(tablero, destino));
	origValido=( (tablero->matriz[origen.x][origen.y]) != 0 );
	destValido=( (tablero->matriz[destino.x][destino.y]) == 0 );

	return (dentro && origValido && destValido);
}
		
int
pertenMat(tipoTablero * tablero, tipoPunto punto)
{
	int v1, v2;

	v1= (punto.x>=0) && (punto.x<tablero->fils);
	v2= (punto.y>=0) && (punto.y<tablero->cols);

	return v1 && v2;
}


int
swap(tipoTablero * tablero, tipoPunto origen, tipoPunto destino)
{
	tablero->matriz[destino.x][destino.y]=tablero->matriz[origen.x][origen.y];
	tablero->matriz[origen.x][origen.y]=0;
	actualizaLibresM(tablero, origen, destino);
	
	return 0;
}

void
actualizaLibresM(tipoTablero * tablero, tipoPunto coordIni, tipoPunto coordFin)
{
	int i;
  
	for(i=0;i<tablero->dim_libres;i++)
		if(tablero->puntos[i].x == coordFin.x && tablero->puntos[i].y==coordFin.y)
		{       tablero->puntos[i].x = coordIni.x;
			tablero->puntos[i].y = coordIni.y;
		}
  
}



void
bolitasAleat(tipoTablero *tablero, tipoParametros param)
{
		
	int i, j, k, l, m=0;
	for(i=0;i<param.bol_agregar;i++)	
	{
		j=rand()%(param.colores)+1; 
		k=rand()%((tablero)->dim_libres-m);
		agrega_bolita(tablero, j,(tablero)->puntos[k].x,(tablero)->puntos[k].y);
		copiaEstruct(&(tablero)->puntos[k],&(tablero)->puntos[tablero->dim_libres-m-1]);
		m++;
	}
	(tablero)->dim_libres-=m;
	tablero->puntos=realloc(tablero->puntos, sizeof(tipoPunto)*(tablero->dim_libres));
}

void
agrega_bolita(tipoTablero *tablero, int r, int  i, int j)
{
	tablero->matriz[i][j]=r;

}
/*copia tipoPunto b , a tipoPunto a*/
void
copiaEstruct(tipoPunto *a, tipoPunto * b)
{
	*a=*b;
}




void
imprimeTablero(tipoJuego *juego, int i)
{
	if(i==1)
	{	printf("Tablero Jugador 1:\n");
		imprTablero(juego->tablero1);
	}
	else
	{
		printf("Tablero jugador 2:\n");
		imprTablero(juego->tablero2);
	}
}
void
imprTablero(tipoTablero *tablero)
{
	int i,j,k;
	k = 0;
	
	printf("      ");

	for(i = 0; i < tablero->cols; i++)
		printf(" %d ",i);

	putchar('\n');

	for(i=0; i<tablero->fils; i++)
        {
		putchar('\n');
		printf(" %3d  ", k++);
		for(j=0; j<tablero->cols; j++)
			switch(tablero-> matriz[i][j])
			{
				case 1: printf(" A "); /* Azul */
					break;
				case 2: printf(" R "); /* Rojo */
					break;
				case 3: printf(" V "); /* Verde */
					break;
				case 4: printf(" N "); /* Naranja */
					break;
				case 5: printf(" P "); /* Purpura */
					break;
				case 6: printf(" C "); /* Celeste */
					break;
				case 7: printf(" B "); /* Blanco */
					break;
				case 8: printf(" G "); /* Gris */
					break;
				case 9: printf(" F "); /* Fucsia */
					break;
				default: printf("   "); /* Negro */ /* Vacio (matriz[i][j] == 0) */
					break;
			}
	}
	putchar('\n');
	putchar('\n');
}

int
creaLibres(tipoTablero *tablero, tipoPunto **puntos)
{
	int i, j, k=0;
	*puntos=NULL;
	tipoPunto *aux;

	for(i=0;i<tablero->fils;i++)
		for(j=0;j<tablero->cols;j++)
		{
			if(k%10==0)
			{	aux=*puntos;
				aux=realloc(aux, sizeof(tipoPunto)*(k+10));
				if(aux==NULL)
					return NO_MEM;
				else
				{
					*puntos=aux;
				}
			}
			if(tablero->matriz[i][j]==0)
			{
				(*puntos)[k].x=i;
				(*puntos)[k++].y=j;	
			}
			
		}
	tablero->dim_libres=k;
	*puntos=realloc(*puntos, sizeof(tipoPunto)*k);
}


void
pideParam(tipoParametros *parametros)
{
  do{
      parametros->filas=getint("Ingrese la cantidad de filas del tablero (minimo 5, maximo 40):\n");
      if(parametros->filas<MIN_DIM || parametros->filas>MAX_DIM)
	printf("Opcion incorrecta, debe seleccionar un numero de filas entre 5 y 40\n");
  }while(parametros->filas<MIN_DIM || parametros->filas>MAX_DIM);
  
  do{
      parametros->columnas=getint("Ingrese la cantidad de columnas del tablero (minimo 5, maximo 40):\n");
       if(parametros->columnas<MIN_DIM || parametros->columnas>MAX_DIM)
	printf("Opcion incorrecta, debe seleccionar un numero de columnas entre 5 y 40\n");
  }while(parametros->columnas<MIN_DIM || parametros->columnas>MAX_DIM);
  
  do{
      parametros->colores=getint("Ingrese la cantidad de colores con los que quiere jugar (minimo 2, maximo 9):\n");
      if(parametros->colores<MIN_COLOR || parametros->colores>MAX_COLOR)
	printf("Opcion incorrecta, debe ingresar un numero de colores entre 2 y 9\n");
  }while(parametros->colores<MIN_COLOR || parametros->colores>MAX_COLOR); 
  
  do{
	parametros->bol_inicial=getint("Ingrese la cantidad de bolillas iniciales (minimo 2, maximo 9):\n");
	if(parametros->bol_inicial<MIN_BOL_INI || parametros->bol_inicial>MAX_BOL_INI)
	printf("Opcion incorrecta, debe ingresar un numero de bolillas iniciales entre 2 y 9\n"); 
  }while(parametros->bol_inicial<MIN_BOL_INI || parametros->bol_inicial>MAX_BOL_INI); 
  
  do{
      parametros->linea=getint("Ingrese la cantidad de bolillas que forman una linea (minimo 3):\n");
      if(parametros->linea<MIN_LINEA || parametros->linea>parametros->filas || parametros->linea>parametros->columnas)
	printf("Opcion incorrecta, debe ingresar el numero de bolillas que conforman una linea con un numero entre 3 y que sea menor al numero de filas y columnas\n"); 
  }while(parametros->linea<MIN_LINEA || parametros->linea>parametros->filas || parametros->linea>parametros->columnas);
  
  do{
	parametros->bol_agregar=getint("Ingrese la cantidad de bolillas a agregar en cada turno (minimo 2, maximo 9):\n");
	 if(parametros->bol_agregar<MIN_TURNO || parametros->bol_agregar>MAX_TURNO)
	printf("Opcion incorrecta, ingrese un numero de bolillas a agregar entre 2 y 9\n"); 
  }while(parametros->bol_agregar<MIN_TURNO || parametros->bol_agregar>MAX_TURNO);


}


int 
pideMem(tipoTablero **tablero, tipoParametros parametros)
{
	int i,j;

	*tablero = malloc(sizeof(tipoTablero));
	if(*tablero == NULL)
		return NO_MEM;

	(*tablero)->matriz = malloc((parametros.filas)*sizeof(int*));
	if((*tablero)->matriz == NULL)
	{
		free(*tablero);
		return NO_MEM;
	}

	for(i = 0; i < parametros.filas; i++)
	{
		(*tablero)->matriz[i] = malloc((parametros.columnas)*sizeof(int));
		if((*tablero)->matriz[i] == NULL)
		{
			for(j = 0; j < i; j++)
				free((*tablero)->matriz[j]);
			free((*tablero)->matriz);
			free(*tablero);
			return NO_MEM;
		}
	}
		return NO_ERROR;
}

int
llenaTablero(tipoJuego *juego)
{
	int ret;
	int i;
	int j;

	if(juego->parametros.opcion==3)
	{ ret=pideMem(&(juego->tablero1), juego->parametros);
	  ret=pideMem(&(juego->tablero2), juego->parametros);
	  if(ret==NO_MEM)
		return ret;
	  inicialTablero((juego->tablero1), juego->parametros);
	  juego->tablero2=juego->tablero1;
	}
	else if(juego->parametros.opcion<3)
	{ 
	  ret=pideMem(&(juego->tablero1), juego->parametros);
	  ret=pideMem(&(juego->tableroUndo), juego->parametros);
	  if(ret==NO_MEM)
		return ret;
	  inicialTablero((juego->tablero1), juego->parametros);
	  juego->tableroUndo=juego->tablero1;
	}
	juego->tablero1->cols=juego->parametros.columnas;
        juego->tablero1->fils=juego->parametros.filas;
         
        return NO_ERROR;
}

void
inicialTablero(tipoTablero *tablero, tipoParametros param)
{
  int i, j, k, l;
  
  for(i=0; i<param.bol_inicial;i++)
  {
    j=(rand()%param.colores)+1;
    k=(rand()%param.filas-1)+1;
    l=(rand()%param.columnas-1)+1;    
    tablero->matriz[k][l]=j;
  }

}

